
# $Id: Point.pm 149 2013-04-13 05:42:04Z chengxin.cai $

use strict;

package Point;

use Switch;
use Carp;
use PerlUtil qw( gen_getter gen_setter );

sub import {
    my $package = shift;
    $package =~ s/.*:://;

    ##export the constructor
    no strict 'refs';
    my ($caller) = caller;
    *{ $caller . '::' . $package } = \&{$package};

    ##install getters and setters
    gen_getter( __PACKAGE__, 'x' );
    gen_getter( __PACKAGE__, 'y' );

    gen_setter( __PACKAGE__, 'set_x' );
    gen_setter( __PACKAGE__, 'set_y' );

    ##other exports
    *{ $caller . '::distance' } = \&distance;
}

sub Point {
    my ( $x, $y ) = @_;

    bless sub {
        my $op = shift;
        switch ($op) {
            case 'x'    { return $x; }
            case 'y'    { return $y; }
            else        { croak 'Invalid operation ' . $op; }
        }
    }, __PACKAGE__;
}

use overload q{""} =>sub {
        my $self = shift;

        sprintf "#<%s x: %s, y: %s>",
            ref $self, $self->x(), $self->y();
};

sub distance {
    my ($p1, $p2) = @_;

    croak 'Invalid reference' . $p1 if ( ref $p1 ne __PACKAGE__ );
    croak 'Invalid reference' . $p2 if ( ref $p2 ne __PACKAGE__ );

    my $square = sub { my $x = shift; $x * $x };

    sqrt( &$square( $p1->x() - $p2->x() )
        + &$square( $p1->y() - $p2->y() ) );
}

1;

